﻿using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc;
using Vin.Event;
using Vin.Extension.Exception.EventArgs;
using Vin.Extension.Exception.Options;
using Vin.Tool.AspNetCore.Singleton;
using Vin.Tool.Core.AssertCategory;
using Vin.Tool.Core.JsonCategory;
using Vin.Tool.Domain.ExceptionModel;

namespace Vin.Extension.Exception;

public class ExceptionTool
{
    /// <summary>
    /// 异常处理
    /// </summary>
    public static async Task HandleExceptionAsync(HttpContext context, int code, object? msg, string? stackTrace,
        CancellationToken? cancellationToken = null)
    {
        var errorMsg = msg is string ? msg.ToString() ?? "请求失败" : msg?.ToCamelCaseJson() ?? "请求失败";
        VinEventManager.Fire<VinExceptionEventArg>(null,
            new VinExceptionEventArg(context, code, errorMsg, stackTrace, context.GetEndpoint()));
        try
        {
            // 序列化去掉空值
            var result = Activator.CreateInstance(VinApp.ApiResultType, code, errorMsg).ToCamelCaseJson();

            // 清空原始的响应流
            if (context.Response.Body.CanSeek)
            {
                await context.Response.Body.FlushAsync();
                context.Response.Body.Seek(0, SeekOrigin.Begin);
                context.Response.Body.SetLength(0);
            }

            context.Response.ContentType = "application/json;charset=utf-8";
            context.Response.StatusCode = StatusCodes.Status200OK;
            // 将错误信息写入响应流
            await context.Response.WriteAsync(result);
        }
        catch (System.Exception ex)
        {
            await context.Response.WriteAsync(ex.Message);
        }
    }

    /// <summary>
    /// 微软异常处理
    /// </summary>
    public static async Task CheckIsMicrosoftExceptionAsync(HttpContext context)
    {
        var responseBody = context.Features.Get<IHttpResponseBodyFeature>();
        if (responseBody is { Stream.CanRead: true })
        {
            responseBody.Stream.Seek(0, SeekOrigin.Begin);
            string body = await new StreamReader(responseBody.Stream).ReadToEndAsync();
            var data = body.ToCamelCaseObject<ValidationProblemDetails>();
            // 验证错误
            if (data.IsNotNull())
            {
                // var exceptionOption = VinApp.GetConfig<ExceptionOption>();
                // var errorMsg = exceptionOption.ShowStackTrace
                //     ? data!.Errors.Count > 0 ? data.Errors.First().Value.First() : data.Title
                //     : "请求出现错误，请检查内容";
                var errorMsg = data!.Errors.Count > 0 ? data.Errors.First().Value.First() : "请求出现错误，请检查内容";


                await ExceptionTool.HandleExceptionAsync(context,
                    data!.Status ?? StatusCodes.Status500InternalServerError,
                    errorMsg, "");
            }
            else
            {
                string msg = context.Response.StatusCode switch
                {
                    StatusCodes.Status400BadRequest => "错误的请求",
                    StatusCodes.Status401Unauthorized => "未授权",
                    StatusCodes.Status403Forbidden => "禁止访问",
                    StatusCodes.Status404NotFound => "未找到服务",
                    StatusCodes.Status405MethodNotAllowed => "请求方法不允许",
                    StatusCodes.Status406NotAcceptable => "无法接受",
                    StatusCodes.Status408RequestTimeout => "请求超时",
                    StatusCodes.Status415UnsupportedMediaType => "不支持的媒体类型",
                    StatusCodes.Status500InternalServerError => "服务器错误",
                    StatusCodes.Status501NotImplemented => "未实现",
                    StatusCodes.Status502BadGateway => "错误的网关",
                    StatusCodes.Status503ServiceUnavailable => "服务不可用",
                    StatusCodes.Status504GatewayTimeout => "网关超时",
                    StatusCodes.Status505HttpVersionNotsupported => "HTTP版本不受支持",
                    StatusCodes.Status506VariantAlsoNegotiates => "变体也会谈判",
                    StatusCodes.Status507InsufficientStorage => "存储空间不足",
                    StatusCodes.Status508LoopDetected => "检测到循环",
                    StatusCodes.Status510NotExtended => "不延伸",
                    StatusCodes.Status511NetworkAuthenticationRequired => "需要网络身份验证",
                    _ => "请联系管理员"
                };
                await ExceptionTool.HandleExceptionAsync(context, context.Response.StatusCode, msg, "");
            }
        }
    }

    /// <summary>
    /// 默认异常处理
    /// </summary>
    public static async Task DefaultExceptionHandlerAsync(HttpContext context)
    {
        var exceptionHandler = context.Features.Get<IExceptionHandlerFeature>();
        if (exceptionHandler != null)
        {
            if (exceptionHandler.Error is ErrorException errorException)
            {
                await ExceptionTool.HandleExceptionAsync(context, errorException.Code,
                    errorException.Message ?? "请联系管理员", errorException.StackTrace);
                return;
            }

            var exceptionOption = VinApp.GetConfig<ExceptionOption>();
            var errorMsg = exceptionOption.ShowStackTrace ? exceptionHandler.Error.Message : exceptionOption.ErrorMsg;

            await ExceptionTool.HandleExceptionAsync(context, context.Response.StatusCode, errorMsg,
                exceptionHandler.Error.StackTrace);
            return;
        }

        if (context.Response.StatusCode != StatusCodes.Status200OK)
        {
            await CheckIsMicrosoftExceptionAsync(context);
        }
    }
}